package com.example.shellcollector.service;

import android.app.Service;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;


import android.content.SharedPreferences;

import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteConstraintException;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;

import com.example.shellcollector.Entity.AppDailyUsage;
import com.example.shellcollector.Entity.AppUsageAggregate;
import com.example.shellcollector.Entity.InstalledAppUsage;
import com.example.shellcollector.database.AppDailyUsageDao;
import com.example.shellcollector.database.InstalledAppUsageDao;
import com.example.shellcollector.util.AppUsageCollectHelper;
import com.example.shellcollector.database.ShellCollectorDatabase;
import com.example.shellcollector.util.ShellPreference;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

//        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
//        long startTime = preferences.getLong("start", 0);
//        long endTime = preferences.getLong("end", 0);
//        if (startTime == 0||endTime == 0) {
//            start = 默认值;
//            end = 默认值（当前时间）;
//        } else {
//            start = startTime;
//            end = endTime;
//        }

public class AppUsageCollectService extends Service {

    private Timer timer;
    private TimerTask task;
    private InstalledAppUsageDao mInstalledAppUsageDao;
    private AppDailyUsageDao mAppDailyUsageDao;
    private static String TAG = "AppUsageCollectService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {


        timer = new Timer();
        task = new TimerTask() {
            @Override
            public void run() {
                if (Looper.myLooper() == null) {
                    Looper.prepare();
                }


                ShellPreference preference = getPreference(getApplicationContext());
//                List<AppUsageEntity> usageInSystem = getAppUsagesFromSystem(preference.getStartTime(), preference.getEndTime());
//                Log.d(TAG, "run: collecting app usage");
//                replaceAll(usageInSystem);

                long currentTime = System.currentTimeMillis();
                long currentDate = AppUsageCollectHelper.getStartTimeOfDayLocal(currentTime);
                List<AppDailyUsage> appDailyUsagesFromSystem = getPastDaysAppDailyUsageFromSystem(
                        currentDate, AppUsageCollectHelper.USAGE_EVENT_TRACK_DAYS_ASSUME);
                Log.d(TAG, "run: collecting app usage");
                insertAppDailyUsagesIntoDB(appDailyUsagesFromSystem);

                // aggregate usage data and update accordingly
                updateInstalledAppUsage();

//                // --------------------------------------
//                // Database test
//                List<AppUsageEntity> usageInDB = getAllUsageFromDB();
//                List<AppUsageEntity> list1 =
//                        mAppUsageDao.getAllOrderByAppName(true);
//                List<AppUsageEntity> list2 =
//                        mAppUsageDao.findByName("b");
//                List<AppUsageEntity> list3 =
//                        mAppUsageDao.getAllOrderByLaunchCount(false);
//                // --------------------------------------
            }
        };

        ShellCollectorDatabase db = ShellCollectorDatabase.getDatabase(getApplicationContext());
        mInstalledAppUsageDao = db.getInstalledAppUsageDao();
        mAppDailyUsageDao = db.getAppDailyUsageDao();
    }

    private void updateInstalledAppUsage() {
        List<AppUsageAggregate> appUsageAggregates = mAppDailyUsageDao.getAppUsageAggregates();
        List<InstalledAppUsage> installedAppUsages = new ArrayList<>();
        for (AppUsageAggregate appUsageAggregate : appUsageAggregates) {
            InstalledAppUsage installedAppUsage = mInstalledAppUsageDao.getAppUsage(appUsageAggregate.appId);
            installedAppUsage.setTotalRunningTime(appUsageAggregate.total);
            installedAppUsage.setLaunchCount(appUsageAggregate.count);

            if (installedAppUsage.getFirstRunningTime() == -1 || appUsageAggregate.first < installedAppUsage.getFirstRunningTime()) {
                installedAppUsage.setFirstRunningTime(appUsageAggregate.first);
            }
            if (installedAppUsage.getLastRunningTime() == -1 || appUsageAggregate.last > installedAppUsage.getLastRunningTime()) {
                installedAppUsage.setLastRunningTime(appUsageAggregate.last);
            }
            installedAppUsages.add(installedAppUsage);
        }
        mInstalledAppUsageDao.update(installedAppUsages);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "start collecting app usage", Toast.LENGTH_SHORT).show();

        timer.scheduleAtFixedRate(task, 0, 5000);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "stop collecting app usage", Toast.LENGTH_SHORT).show();
        task.cancel();
        timer.cancel();
    }

    // -----------------------------------
    // utility functions
    // -----------------------------------

    private ShellPreference getPreference(Context context) {
        long start;
        long end;

        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        long startTime = preferences.getLong("start", 0);
        long endTime = preferences.getLong("end", 0);
        if (startTime == 0 || endTime == 0) {
            start = AppUsageCollectHelper.getPreviousDaysTimestamp(7);
            end = System.currentTimeMillis();
        } else {
            start = startTime;
            end = endTime;
        }

        ShellPreference preference = new ShellPreference();
        preference.setStartTime(start);
        preference.setEndTime(end);

        return preference;
    }

    /**
     * Collect the app daily usages in the past a few days.
     * @param endDate end timestamp at 00:00 of some date (GMT+8)
     * @param daysAgo how many days ago to start calculating
     * @return a list of app daily Usage objects ready to be inserted to database
     */
    private List<AppDailyUsage> getPastDaysAppDailyUsageFromSystem(long endDate, int daysAgo) {
        long startTime = endDate - daysAgo * AppUsageCollectHelper.MILLISECONDS_PER_DAY;
        List<AppDailyUsage> appDailyUsages = new ArrayList<>();
        for (long currentTime = startTime; currentTime <= endDate;
             currentTime += AppUsageCollectHelper.MILLISECONDS_PER_DAY) {
            appDailyUsages.addAll(getAppDailyUsagesFromSystem(currentTime));
        }

        return appDailyUsages;
    }

    /**
     * Collect the app daily usages in a past given range.
     * @param startDate start timestamp at 00:00 of some date (GMT+8)
     * @param endDate end timestamp at 00:00 of some date (GMT+8)
     * @return a list of app daily Usage objects ready to be inserted to database
     */
    private List<AppDailyUsage> getPastDaysAppDailyUsageFromSystem(long startDate, long endDate) {
        if (startDate > endDate) {
            throw new InvalidParameterException("start date shall never be bigger than end date");
        }
        int daysAgo = (int)( endDate - startDate ) / AppUsageCollectHelper.MILLISECONDS_PER_DAY;
        return getPastDaysAppDailyUsageFromSystem(endDate, daysAgo);
    }

    /**
     * Query usage events stored in android system and accumulate total running time per app.
     *
     * @param startTime query range start timestamp
     * @return a list of app daily usage entities from start_time to start_time + 24 hours, running time accumulated
     */
    private List<AppDailyUsage> getAppDailyUsagesFromSystem(long startTime) {

//        startTime = AppUsageCollectHelper.getStartTimeOfDayLocal(startTime);

        HashMap<String, AppDailyUsage> appDailyUsages = new HashMap<>();
        HashMap<String, List<UsageEvents.Event>> sameEvents = new HashMap<>();
        UsageStatsManager usageStatsManager = getSystemService(UsageStatsManager.class);
        PackageManager packageManager = getPackageManager();

        AppUsageCollectHelper.groupAppDailyUsageEvents(
                usageStatsManager, startTime, appDailyUsages, sameEvents);

        AppUsageCollectHelper.collectAppDailyUsageFromMaps(startTime, appDailyUsages, sameEvents);

        // set appId
        for (Map.Entry<String, AppDailyUsage> entry : appDailyUsages.entrySet()) {
            String packageName = entry.getKey();
            AppDailyUsage appDailyUsage = entry.getValue();
            if (mInstalledAppUsageDao.isAppInstalled(packageName)) {
                appDailyUsage.setAppId(mInstalledAppUsageDao.getAppUsage(packageName).getId());
            } else {
                appDailyUsage.setAppId(-1); // -1 represents app_id not set
            }
        }

        List<AppDailyUsage> resultList = new ArrayList<>(appDailyUsages.values());
        resultList.removeIf(appDailyUsage -> appDailyUsage.getAppId() == -1);

        return resultList;
    }

//    /**
//     * Query usage events stored in android system and accumulate total running time per app.
//     *
//     * @param startTime query range start timestamp
//     * @param endTime   query range end timestamp
//     * @return a list of app usage entities from start_time to end_time, running time accumulated
//
//     */
//    private List<AppUsageEntity> getAppUsagesFromSystem(long startTime, long endTime) {
//
//        startTime = AppUsageCollectHelper.getStartTimeOfDayLocal(startTime);
//        endTime = AppUsageCollectHelper.getStartTimeOfDayLocal(endTime);
//
//        HashMap<String, AppUsageEntity> appUsages = new HashMap<>();
//        HashMap<String, List<UsageEvents.Event>> sameEvents = new HashMap<>();
//        UsageStatsManager usageStatsManager = getSystemService(UsageStatsManager.class);
//        PackageManager packageManager = getPackageManager();
//
//        // groupAppUsageEvents(startTime, endTime, appUsages, sameEvents);
//        AppUsageCollectHelper.groupAppUsageEvents(
//                usageStatsManager, startTime, endTime, appUsages, sameEvents);
//
//        // collectAppUsageFromMaps(startTime, endTime, appUsages, sameEvents);
//        AppUsageCollectHelper.collectAppUsageFromMaps(
//                startTime, endTime, appUsages, sameEvents);
//
//        List<AppUsageEntity> appUsageEntities = new ArrayList<>(appUsages.values());
//        // loadAppLabels(appUsageEntities);
//        AppUsageCollectHelper.loadAppLabels(packageManager, appUsageEntities);
//
//        return appUsageEntities;
//    }

    private void insertAppDailyUsagesIntoDB(List<AppDailyUsage> appDailyUsages) {
        appDailyUsages.removeIf(
                appDailyUsage -> mAppDailyUsageDao.isAppDailyUsageExists(
                        appDailyUsage.getAppId(), appDailyUsage.getDate()));
        try {
            mAppDailyUsageDao.insertAll(appDailyUsages);
        } catch (SQLiteConstraintException e) {
            String msg = e.getMessage();
            throw e;
        }
    }
}
